/*
 * This class defines a common interface of pieces in all tetris-like games
 */
package sirtet;

/**
 * An abstract game piece with ElementType as the type of a single block
 * @author guangwei.zhu
 */
public abstract class Piece<ElementType> implements Cloneable {

    protected Object pieceData[][];
    protected int pieceWidth, pieceHeight;

    /**
     * Get an empty element of ElementType which represents an unoccupied block
     * @return an empty element
     */
    @SuppressWarnings({"unchecked"})
    public abstract ElementType getEmptyElement();

    protected void initElements() {
        int i, j;

        for (i = 0; i < pieceHeight; i++) {
            for (j = 0; j < pieceWidth; j++) {
                pieceData[i][j] = this.getEmptyElement();
            }
        }
    }

    // Suppress unchecked warning for generic array creation
    protected Piece(int width, int height) {
        pieceData = new Object[height][width];
        pieceHeight = height;
        pieceWidth = width;

        initElements();
    }

    /** Detects if two pieces are exactly the same (not including mirrors
     *  or rotations
     *  @param obj another piece to compare with
     */
    @Override
    public boolean equals(Object obj) {
        if (obj.getClass() != this.getClass()) {
            return false;
        }   // Not pieces of the same type

        Piece piece = (Piece) obj;
        if (piece.pieceWidth != this.pieceWidth ||
                piece.pieceHeight != this.pieceHeight) {
            return false;
        }   // Not the same size

        int i, j;

        for (i = 0; i < pieceHeight; i++) {
            for (j = 0; j < pieceWidth; j++) {
                if (piece.pieceData[i][j] != this.pieceData[i][j]) {
                    return false;
                }
            }
        }

        return true;
    }
    
    /**
     * Get the width of the piece
     * @return width of the piece
     */
    public int getWidth() {
        return pieceWidth;
    }

    /**
     * Get the height of the piece
     * @return height of the piece
     */
    public int getHeight() {
        return pieceHeight;
    }

    /**
     * Compute a hash code for the object
     * @return hash code for the object
     */
    @Override
    public int hashCode() {
        int hash = this.pieceData.hashCode();
        hash = 89 * hash + this.pieceWidth * 17 + this.pieceHeight;
        return hash;
    }

    /**
     * Create a cloned datablock which contains information about blocks
     * @return a cloned copy of piece_data[][]
     */
    @SuppressWarnings({"unchecked"})
    protected Object[][] cloneData() {
        int i;
        Object[][] dataCopy = new Object[pieceHeight][pieceWidth];

        for (i = 0; i < pieceHeight; i++) {
            System.arraycopy(pieceData[i], 0, dataCopy[i], 0, pieceWidth);
        }

        return dataCopy;
    }

    /**
     * Return the block/element at position (x,y) of the piece
     * @return the block/element at position (x,y)
     */
    @SuppressWarnings({"unchecked"})
    public ElementType getElement(int x, int y) {
        return (ElementType) pieceData[y][x];
    }
}
